#ifndef _object_h_
#define _object_h_

// object.h
//
//    Base class to add debugging to user objects

//Disable the warning regarding long template names
#ifdef WIN32
#pragma warning(disable:4786)
#pragma warning(disable:4503)
#endif


#include "objectMgr.h"
#include "stringTools.h"

#include <string>
#include <map>
#include <iostream>
#include <typeinfo>

#include <stdio.h> // sprintf()


namespace simple{
    //
    // ObjectInfoBase
    //
    // Base class for ObjectInfo. A single map<> template is used
    // to prevent a proliferation of unnecessary template objects.


    class ObjectInfoBase
    {
    public:
        typedef std::map<void*, char> INSTANCEMAP;

        ObjectInfoBase () : _debug (0) {}

        int  debug ()      { return _debug;}
        void debug (int d) { _debug = d;}
        // Get/set the debugging level for this object

        bool isDebug (int level) { return _debug >= level;}
        // Return true if debugging is enabled for this object

        void addInstance    (void* obj) { _mapping[obj] = 1;}
        void removeInstance (void* obj) { _mapping.erase (obj);}
        // Add/remove an object from our instance list

        virtual std::string process (const std::string& command) = 0;
        // Command processor. ObjectMgr can send a command
        // string and expects a response string in return.

        virtual std::string dump () = 0;
        // Allow derived classes to produce a list of managed objects
        // This is abstract because our derived class has the
        // implementation, but ObjectMgr has a pointer to a
        // ObjectInfoBase object.

    protected:
        int         _debug;   // Debug level for this object
        INSTANCEMAP _mapping; // List of all current objects
    };




    //
    // ObjectInfo
    //
    // A single ObjectInfo class is created for each Object class
    // (if configured). This object contains debug information for
    // the local class as well as hooks itself onto the ObjectMgr
    // list.

    template <class T>
    class ObjectInfo : public ObjectInfoBase
    {
    public:
        static ObjectInfo<T>& gOnly ();
        //singleton object

        const std::string& name () const { return _name;}
        // Return our object name. This might be as simple
        // as what typeid() returns, but it also can be
        // modified as necessary.

        virtual std::string process (const std::string& command);
        // Process the debugging command and return results.
        // Strings are used to keep the interface generic.
        // The base class version ignores everything

        virtual std::string dump ();
        // Produce a space separated list of object instances

    private:
        static ObjectInfo<T>* _sOnly;
        std::string _name;

        ObjectInfo ();
    };


    template <class T>
    ObjectInfo<T>* ObjectInfo<T>::_sOnly = 0;

    template <class T>
    ObjectInfo<T>& ObjectInfo<T>::gOnly ()
    {
        if (!_sOnly)
            _sOnly = new ObjectInfo;

        return *_sOnly;
    }


    template <class T>
    simple::ObjectInfo<T>::ObjectInfo () //ObjectInfo<T>::ObjectInfo<T>
    {
        // Setup our object name. This is compiler and platform
        // dependent so this function can be modified to create
        // a more uniform string
        _name = typeid(T).name();

        // Add this class to our ObjectMgr list
        ObjectMgr::gOnly().add (_name, this);
    }



    template <class T>
    std::string ObjectInfo<T>::dump ()
    {
        std::string str;
        char buffer[16];

        INSTANCEMAP::iterator i;
        for (i=_mapping.begin(); i != _mapping.end(); i++) {
            sprintf (buffer, " %d", i->first);
            str += buffer;
        }

        return str;
    }


    // Our original stub version
    // template <class T>
    // std::string ObjectInfo<T>::process (const std::string& command)
    // {
    //     //TODO
    //     return "";
    // }


    template <class T>
    std::string ObjectInfo<T>::process (const std::string& command)
    {
        // Parse our command
        Token token = StringTools::sParse (command);

        // Decide what to do with it. With only a few commands just use
        // if statements.
        INSTANCEMAP::iterator i;
        std::string result;             // Our result string
        char buffer[16];                    // Buffer for sprintf()

        if (token.parsed == "list") {
            // "list" Return a space separated list of all instances (by address)
            for (i=_mapping.begin(); i != _mapping.end(); i++) {
                sprintf (buffer, "%x ", i->first);
                result += buffer;
            }
        }
        else if (token.parsed == "execute") {
            // "execute <command>" Send the remainder of the string to all instances
            for (i=_mapping.begin(); i != _mapping.end(); i++) {
                T* obj = reinterpret_cast<T*>(i->first);
                result += obj->process (token.remainder);
                result += " ";
            }
        }
        else if (token.parsed == "to") {
            // "to <instance> <command>" Send the remainder of the string to a
            // specific instance. Our match will be by string because the list
            // command returns a list of strings
            Token instance = StringTools::sParse (token.remainder);
            for (i=_mapping.begin(); i != _mapping.end(); i++) {
                sprintf (buffer, "%x", i->first);
                if (instance.parsed == buffer) {
                    T* obj = reinterpret_cast<T*>(i->first);
                    result += obj->process (instance.remainder);
                }
            }
        }
        else {
            // Unknown command. Don't do anything
        }

        return result;
    }


    //
    // Object
    //
    // Options:
    //   eNone          No options
    //   eTrackInstance Track this object instance (default)

    template <class T>
    class Object
    {
    public:
        enum eOptions {eNone=0, eTrackInstance=1};

        Object (eOptions options = eTrackInstance);
        // Object construction with optional arguments

        virtual ~Object ();

        int  debug ()      { return ObjectInfo<T>::gOnly().debug();}
        void debug (int d) { ObjectInfo<T>::gOnly().debug(d);}
        // Get/set the debugging level for this object

        bool isDebug (int level)
        { return ObjectInfo<T>::gOnly().isDebug(level);}
        // Return true if debugging is enabled for this level

        void debugMessage (const std::string& msg);
        // Output the debugMessage.

        virtual std::string header () const;
        // The header string printed before any debugging messages

        virtual std::string process (const std::string& command);
        // Process a debugging command and return results.
        // Strings are used to keep the interface generic.
        // The base class version ignores everything
    };



    template <class T> Object<T>::Object (eOptions options)
    {
        // Add ourself to our instance list if enabled
        if (options & eTrackInstance) {
            ObjectInfo<T>::gOnly().addInstance (this);
        }
    }

    template <class T> Object<T>::~Object ()
    {
        ObjectInfo<T>::gOnly().removeInstance (this);
    }


    template <class T>
    void Object<T>::debugMessage (const std::string& msg)
    {
        ObjectMgr::gOnly().debugMessage (header(), msg);
    }


    template <class T>
    std::string Object<T>::header () const
    {
        const T* obj = static_cast<const T*> (this);

        char buffer[32];
        sprintf (buffer, " (0x%0x): ", obj);
        std::string h = ObjectInfo<T>::gOnly().name();
        h += buffer;
        return h;
    }


    template <class T>
    std::string Object<T>::process (const std::string& /*command*/)
    {
        // The base class ignores all commands
        return "";
    }

}
#endif // _object_h_
